﻿using System;
using System.Messaging;

namespace Pixeldyne.Core
{
	/// <summary>
	/// MSMQ utility for sending and receiving messages.
	/// </summary>
	public class MessageQueueAdapter : IDisposable
	{
		private readonly MessageQueue mq = null;
		private volatile bool alreadyDisposed = false;

		/// <summary>
		/// Gets the queue name.
		/// </summary>
		public string QueueName
		{
			get;
			private set;
		}

		/// <summary>
		/// Default constructor using a queue name.
		/// </summary>
		/// <param name="queueName">Name of message queue to use.</param>
		
		public MessageQueueAdapter(string queueName)
		{
			MessageQueue.EnableConnectionCache = false;

			QueueName = queueName;
			mq = new MessageQueue(QueueName);
		}

		/// <summary>
		/// Default destructor.
		/// </summary>
		~MessageQueueAdapter()
		{
			Dispose(false);
		}

		/// <summary>
		/// Disposes of the resources.
		/// </summary>
		public void Dispose()
		{
			Dispose(true);
			GC.SuppressFinalize(this);
		}

		/// <summary>
		/// Disposes of the resources.
		/// </summary>
		/// <param name="isDisposing"></param>
		protected virtual void Dispose(bool isDisposing)
		{
			if (alreadyDisposed) return;

			if (isDisposing)
			{
				if (mq != null)
				{
					mq.Close();
				}
			}

			alreadyDisposed = true;
		}

		/// <summary>
		/// Closes the queue.
		/// </summary>
		public void Close()
		{
			mq.Close();
		}

		/// <summary>
		/// Gets an array of all messages on the queue.
		/// </summary>
		/// <returns></returns>
		public Message[] GetAllMessages()
		{
			return mq.GetAllMessages();
		}

		/// <summary>
		/// Sends a message to the queue.
		/// </summary>
		/// <param name="message">Message object.</param>
		/// <returns>Boolean value indicating success.</returns>
		public bool SendMessage(object message)
		{
			return SendMessage(message, null);
		}

		/// <summary>
		/// Sends a message to the queue.
		/// </summary>
		/// <param name="message">Message object.</param>
		/// <param name="label">Message label.</param>
		/// <returns>Boolean value indicating success.</returns>
		public bool SendMessage(object message, string label)
		{
			Message msg = new Message(message, new XmlMessageFormatter()) {Priority = MessagePriority.High};

			if (!string.IsNullOrEmpty(label))
				msg.Label = label;

			lock (mq)
			{
				if (mq.CanWrite)
				{
					mq.Send(msg);
					return true;
				}
			}

			return false;
		}

		/// <summary>
		/// Starts receiving messages.
		/// </summary>
		/// <param name="handler">Message handler.</param>
		public void StartReceive(ReceiveCompletedEventHandler handler)
		{
			// open the selected message queue
			mq.MessageReadPropertyFilter.SetAll();

			// the target types we have stored in the message body
			((XmlMessageFormatter)mq.Formatter).TargetTypes = new[] { typeof(string) };

			// set the event handler to be called when the message has been received
			mq.ReceiveCompleted += handler;

			// start the receive message process; this call returns immediately
			IAsyncResult MQResult = mq.BeginReceive(TimeSpan.FromMinutes(1), mq);
		}

		private void MessageEventHandler(object sender, ReceiveCompletedEventArgs e)
		{
			// get the message from the queue
			Message queueMessage = ((MessageQueue)e.AsyncResult.AsyncState).EndReceive(e.AsyncResult);

			// process the message
			string message = (string)queueMessage.Body;

			// start looking for the next message
			IAsyncResult AsyncResult = ((MessageQueue)e.AsyncResult.AsyncState).BeginReceive(
				new TimeSpan(1, 0, 0), e.AsyncResult.AsyncState);
		}

	}
}